// Changement : on exporte que les donnees, c'est povray qui fabriquera ses objets


import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.Locale;
import java.util.Random; 
import java.util.TreeSet;



import pluginUtils.AGCircle;
import pluginUtils.Circle;
import pluginUtils.CircleComparator;
import pluginUtils.CircleInversion;
import pluginUtils.Complexe; 
import pluginUtils.Point2D;
import pluginUtils.SoddyTriplet;



public class ApollonianGasketV2 extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	
	private PrintStream output;
	private double xShift=0; 
	private double zShift=0; 
	
	private TreeSet<AGCircle> enAttente=new TreeSet<AGCircle>(new CircleComparator()); 
	private double decale=4.5; 
	private CircleInversion cinv=new CircleInversion(new Circle(Complexe.createComplexeFromRI(decale, decale),0.05)); 
 
	
	

	private Random generator=new Random(); // One always needs some randomness
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private String[] palette={"Yellow","Red","Blue","Green","White","NavyBlue","Turquoise","Orange"};
	private Color[] palette1={Color.YELLOW,Color.RED,Color.ORANGE,Color.BLUE,Color.GREEN,Color.ORANGE};
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	private void printCircleDescription(AGCircle c){
		//if(intersect(c))return; 
		
		//if(c.getRadius()<0.1) return; 
		int intX=(int)(c.getCenter().getReal()*acoef+ccoef);
		int intY=(int)(c.getCenter().getIm()*vcoef+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		
	
			
			double Xs=c.getCenter().getReal(); 
			double Ys=c.getCenter().getIm(); 
			double rapp=Xs*Xs+Ys*Ys+1; 
			double u=Xs/rapp; 
			double v=Ys/rapp; 
			double w=(Xs*Xs+Ys*Ys)/rapp;
			
			double Xsr=c.getCenter().getReal(); 
			double Ysr=c.getCenter().getIm()+c.getRadius(); 
			double rappr=Xsr*Xsr+Ysr*Ysr+1; 
			double ur=Xsr/rappr; 
			double vr=Ysr/rappr; 
			double wr=(Xsr*Xsr+Ysr*Ysr)/rappr;
		
			double nrad=(u-ur)*(u-ur)+(v-vr)*(v-vr)+(w-wr)*(w-wr);
			//nrad=Math.sqrt(nrad)*1.414; 
			
			String s="sphere{<"+u+","+v+","+w+">,";
			
			
			s+=nrad; 
			s+=" texture{T1} finish{F1}}"; 
			output.println(s);
		
	}
		
	
	private void drawCircle(AGCircle c){
		double xCenter=c.getCenter().getReal(); 
		double yCenter=c.getCenter().getIm(); 
		//if(c.getRadius()<0.001) return ; 
		
		int intX=(int)((xCenter-xShift)*acoef+ccoef);
		int intY=(int)((yCenter-zShift)*vcoef+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		
		primeIP.setColor(palette1[c.getColor()]);
		primeIP.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);
	}
		
		
	private void drawPoint(double x,double y){
		int radi=15; 
		int intX=(int)(x*acoef+ccoef);
		int intY=(int)(y*vcoef+wcoef);
		primeIP.fillOval(intX-radi,intY-radi,2*radi,2*radi);
	}
	
	
	
	private void drawTriangle(double[] X,double[] Y){
		int xint[]=new int[3]; 
		int yint[]=new int[3];
		for(int i=0;i<3;i++){
			xint[i]=(int)(X[i]*acoef+ccoef); 
			yint[i]=(int)(Y[i]*vcoef+wcoef);
		}
		primeIP.fillPolygon(new Polygon(xint,yint,3)); 
		
	}
	
	
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	
	private void drawLine(Point2D p,Point2D q){
		int XD=(int)(p.getX()*acoef+ccoef); 
		int YD=(int)(p.getY()*vcoef+wcoef); 
		int XF=(int)(q.getX()*acoef+ccoef); 
		int YF=(int)(q.getY()*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	/** On choisit un cercle, on y place trois cercles, et on lance l'algo **/
	private void makeGasket(AGCircle origin){
		AGCircle newOrigin=new AGCircle(origin.getCenter(),origin.getRadius(),true,origin.getColor()); 
		double dx=newOrigin.getXcenter(); 
		double dy=newOrigin.getYcenter(); 
		double radius=newOrigin.getRadius()*(3/(2*Math.sqrt(3)+3)); 
		double h=Math.sqrt(3)*radius; 
		double dc=2*h/3; 
		double angle=0;
		AGCircle ca=new AGCircle(Complexe.createComplexeFromRI(dc*Math.cos(angle)+dx,dc*Math.sin(angle)+dy),radius,false,1);
		//drawCircle(ca); 
		AGCircle cb=new AGCircle(Complexe.createComplexeFromRI(dc*Math.cos(2*Math.PI/3+angle)+dx, dc*Math.sin(2*Math.PI/3+angle)+dy),radius,false,1);
		//drawCircle(cb); 
		AGCircle cc=new AGCircle(Complexe.createComplexeFromRI(dc*Math.cos(4*Math.PI/3+angle)+dx, dc*Math.sin(4*Math.PI/3+angle)+dy),radius,false,1);
		//drawCircle(cc); 
		ArrayList<SoddyTriplet> myLocalSoddyTriplets=new ArrayList<SoddyTriplet>(); 
		myLocalSoddyTriplets.add(new SoddyTriplet(ca,cb,cc)); 
		SoddyTriplet su=new SoddyTriplet(newOrigin, ca,cb); 
		myLocalSoddyTriplets.add(su); 
		SoddyTriplet sv=new SoddyTriplet(newOrigin,cc,cb); 
		myLocalSoddyTriplets.add(sv);
		SoddyTriplet sw=new SoddyTriplet(newOrigin, cc,ca); 
		myLocalSoddyTriplets.add(sw);
		while(myLocalSoddyTriplets.size()!=0){
			ArrayList<SoddyTriplet> pro=new ArrayList<SoddyTriplet>();
		for(SoddyTriplet toto:myLocalSoddyTriplets){
			AGCircle nouvo=toto.getMini();
			if((nouvo.getRadius()>=0.005)&&(nouvo.getRadius()<3)){
				//drawCircle(nouvo);
				enAttente.add(nouvo); 
				balance.add(nouvo); 
				AGCircle a=toto.getA(); 
				AGCircle b=toto.getB(); 
				AGCircle c=toto.getC();
				if(!balance.contains(a)&&(a.getRadius()>0.005)) balance.add(a); 
				if(!balance.contains(b)&&(b.getRadius()>0.005)) balance.add(b); 
				if(!balance.contains(c)&&(c.getRadius()>0.005)) balance.add(c); 
			    pro.add(new SoddyTriplet(a,b,nouvo)); 
				pro.add(new SoddyTriplet(a,c,nouvo));
			    pro.add(new SoddyTriplet(c,b,nouvo));		
			} //if
			//else drawCircle(nouvo);
			else enAttente.add(nouvo); 
		}
		myLocalSoddyTriplets=pro; 
		//System.out.println(pro.size());
		} // while
	}
	
	private ArrayList<AGCircle> aTraiter=new ArrayList<AGCircle>();
	private ArrayList<AGCircle> balance=new ArrayList<AGCircle>(); 
	
	public void launch(){
		for(int i=0;i<6;i++){
			System.out.println(i+" "+aTraiter.size()); 
			balance=new ArrayList<AGCircle>(); 
			for(AGCircle toto:aTraiter){
				makeGasket(toto); 
			}
			aTraiter=balance; 
		}
	}
	
	public void run(String arg) {
		primeIP = new ColorProcessor(X,Y);
		
		
		
	
		double limit=6; 
		double lmoins=3;
		
		
		setBounds(lmoins,limit,lmoins,limit);
		//setBounds(-0.25,0.5,-0.25,0.5); 
		setCoefs(); 
	
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(1); 
		

		setProcessor("Verifying", primeIP);
		
	
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(3);
	
		primeIP.setColor(Color.BLACK);
		
		double rayon=1.5; // radius of big circle
	
		AGCircle init=new AGCircle(Complexe.createComplexeFromRI(decale,decale),rayon,true,0); 
		aTraiter.add(init); 
		launch();
		try{
			//output=new PrintStream("F:/Povray/outApollo2.txt");
			output=new PrintStream("C:/users/decomite/pictures/outApollo.txt"); 
			}
			catch(Exception e){System.out.println("Probleme 2 "+e); System.exit(0);}
		for(AGCircle c:enAttente){
			drawCircle(c);
			output.print((c.getXcenter()-decale)+","+(c.getYcenter()-decale)+","+c.getRadius()+","+c.getColor()+","); 
		}
		output.close();
		show(); 
	}	
}
